package com.springboot.frame.socket.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author liheng
 * @ClassName ServerHandler
 * @Description
 * @date 2020-09-17 11:08
 * Exception
 * @sharable handler, so can't be added or removed multiple times
 * <p>
 * 因是通过注入的方式，而不是通过new
 * <p>
 * 解决方案：
 * <p>
 * 增加@Sharable注解
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class ServerNettyHandler extends SimpleChannelInboundHandler<String> {


    /**
     * 心跳丢失次数
     */
    private int counter = 0;

    /**
     * 功能：当从Channel中读数据时被调用
     *
     * @param ctx
     * @param body
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String body) throws Exception {
        Channel channel = ctx.channel();
        System.out.println("Client say : " + body +channel.toString());
        /**发送心跳,保持长连接*/
        String s = "pong$_";
        ctx.channel().writeAndFlush(s);
//        log.debug("心跳收到!");
//        System.out.println("心跳收到!");
        //重置心跳丢失次数
        counter = 0;
    }

    /**
     * 功能：读取完毕客户端发送过来的数据之后的操作
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.info("服务端接收数据完毕..");
        super.channelReadComplete(ctx);
    }

    /**
     * 功能：服务端发生异常的操作
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 不关闭，打印日志   ctx.close();
        log.error("异常信息：\r\n" + cause.getMessage());
        super.exceptionCaught(ctx, cause);
    }

    /**
     * 添加channel
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        log.info(channel.remoteAddress() + " 客户端已连接");
        super.handlerAdded(ctx);

    }

    /**
     * 移除 channel
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        log.info(channel.remoteAddress() + " 客户端已移除");
        super.handlerRemoved(ctx);
    }

    /**
     * 当Channel变成活跃状态时被调用；Channel是连接/绑定、就绪的
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().localAddress().toString() + " 通道已激活");
        super.channelActive(ctx);
    }

    /**
     * Channel未连接到远端
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
        Channel channel = ctx.channel();
        log.info(channel.localAddress().toString() + "掉线");
        channel.close();
        ctx.close();
    }


    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state().equals(IdleState.READER_IDLE)){
                // 空闲40s之后触发 (心跳包丢失)
                if (counter >= 3) {
                    // 连续丢失3个心跳包 (断开连接)
                    ctx.channel().close().sync();
                    log.error("已与"+ctx.channel().remoteAddress()+"断开连接");
                    System.out.println("已与"+ctx.channel().remoteAddress()+"断开连接");
                } else {
                    counter++;
                    log.debug(ctx.channel().remoteAddress() + "丢失了第 " + counter + " 个心跳包");
                    System.out.println("丢失了第 " + counter + " 个心跳包");
                }
            }

        }
    }
}
